/**
 * 
 */
package edu.arizona.bio5.onto.decomposition.persistent.rdf;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

import org.openrdf.model.URI;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQuery;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;

import uk.ac.manchester.cs.graph.DirectedGraph;
import uk.ac.manchester.cs.owlapi.modularity.ModuleType;
import edu.arizona.bio5.onto.decomposition.Atom;
import edu.arizona.bio5.onto.decomposition.persistent.PersistentAtom;
import edu.arizona.bio5.onto.decomposition.persistent.PersistentAtomicDecompositionImpl;

/**
 * @author Pavel Klinov
 *
 */
public class SesameAtomicDecompositionImpl extends 	PersistentAtomicDecompositionImpl {

	private static final String PREFIX = 	"PREFIX ad: <" + RDFADConstants.AD_BASE_NS + "> \n" + 
			"PREFIX ads: <" + RDFADConstants.AD_BASE_SCHEMA + "> \n" +
			"PREFIX rdf: <" + RDF.NAMESPACE + "> \n\n";	
	
	private static final String AD_ATOMS_FOR_TERM_QUERY = PREFIX + 
			"SELECT DISTINCT ?atom FROM \n" +
			"<ng> \n" + 
			"WHERE {\n " +
			"?atom rdf:type ads:Atom .\n" +
			"?atom ads:hasLabel ?label .\n" +
			"?label rdf:type ads:AtomSignatureLabel .\n" +
			"?label ads:hasSignature ?sig .\n" +
			"?sig ads:hasTerm ?term .\n" +
			"}";	
	
	private final Repository mRepo;
	private RepositoryConnection mConn = null;
	
	public SesameAtomicDecompositionImpl(Repository repo, java.net.URI ontoURI, DirectedGraph<Atom, Object> adGraph, Map<java.net.URI, Set<Atom>> termIndex, ModuleType type) {
		super(ontoURI, adGraph, termIndex, type);
		mRepo = repo;
	}
	
	SesameAtomicDecompositionImpl(Repository repo, java.net.URI ontoURI, ModuleType type) {
		this(repo, ontoURI, null, null, type);
	}

	@Override
	public void close() {
		closeConnection();
		
		super.close();
	}
	
	protected RepositoryConnection getConnection() throws RepositoryException {
		if (mConn != null && mConn.isOpen()) {
			return mConn;
		}
		else {
			return mConn = mRepo.getConnection();
		}
	}
	
	private void closeConnection() {
		try {
			if (mConn != null) {
				mConn.close();
			}
		} catch (RepositoryException e) {}
	}

	protected void setADGraph(DirectedGraph<Atom, Object> adGraph) {
		super.setADGraph(adGraph);
	}
	
	@Override
	public Collection<Atom> getAtomsForTerm(java.net.URI termURI) {
		Collection<Atom> atoms = new ArrayList<Atom>();
		
		try {
			mConn = mRepo.getConnection();
			
			TupleQuery atomQuery = getQuery(AD_ATOMS_FOR_TERM_QUERY);
			
			atomQuery.setBinding("term", mConn.getValueFactory().createURI(termURI.toString()));
			
			TupleQueryResult results = atomQuery.evaluate();
			
			while(results.hasNext()) {
				URI atomURI = (URI)results.next().getBinding("atom").getValue();
				//create a persistent atom
				//atom's id is the last part of its URI
				PersistentAtom atom = (PersistentAtom)getAtomByID(SesameADLoader.getAtomID(atomURI));
				
				if (atom != null) {
					atoms.add(atom);
				}
			}
			
			return atoms;
			
		} catch (RepositoryException e) {
			// TODO Log it
			return Collections.emptySet();
		} catch (MalformedQueryException e) {
			throw new RuntimeException(e);
		} catch (QueryEvaluationException e) {
			throw new RuntimeException(e);
		}
		finally {
			closeConnection();
		}
	}

	private TupleQuery getQuery(String queryString) throws RepositoryException, MalformedQueryException {
		String qString = queryString.replaceAll("<ng>", "<" + RDFADConstants.AD_BASE_NS + getOntologyURI() + ">");
		
		return mConn.prepareTupleQuery(QueryLanguage.SPARQL, qString);
	}	
	
}
